# higher cmake version is required as Kokkos needs it
CMAKE_MINIMUM_REQUIRED(VERSION 3.16)

# Project TINES
PROJECT(TINES VERSION 1.0.3 LANGUAGES C CXX)

# Built-in modules
INCLUDE(GNUInstallDirs)
INCLUDE(CMakePackageConfigHelpers)

# CMAKE setup
#SET(CMAKE_VERBOSE_MAKEFILE ON) # see all output
SET(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR})

# Define BUILD_TESTING in cmake
INCLUDE(CTest)

# Installation
IF (CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
  SET(CMAKE_INSTALL_PREFIX "${PROJECT_BINARY_DIR}" CACHE PATH "FOO install prefix" FORCE)
ENDIF()

IF (NOT CMAKE_INSTALL_LIBDIR)
  SET(CMAKE_INSTALL_LIBDIR lib)
ENDIF()
MESSAGE(STATUS "CMAKE_INSTALL_LIBDIR is set ${CMAKE_INSTALL_LIBDIR}")

SET(TINES_INSTALL_LIB_PATH     ${CMAKE_INSTALL_LIBDIR})
SET(TINES_INSTALL_BIN_PATH     bin)
SET(TINES_INSTALL_INCLUDE_PATH include/tines)
SET(TINES_INSTALL_TEST_PATH    unit-test)
SET(TINES_INSTALL_EXAMPLE_PATH example)

LIST(APPEND CMAKE_MODULE_PATH "${TINES_SOURCE_DIR}/core/cmake/modules")
MESSAGE(STATUS "TINES uses CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH}")

#
# Options : use TINES prefix
#
OPTION(TINES_ENABLE_INSTALL "Flag to enable install exports for TINES" ON)
OPTION(TINES_ENABLE_SHARED_BUILD "Flag to enable unit examples" OFF)
OPTION(TINES_ENABLE_EXAMPLE "Flag to enable unit examples" OFF)
OPTION(TINES_ENABLE_TEST "Flag to enable unit tests" OFF)
OPTION(TINES_ENABLE_VERBOSE "Flag to enable TINES verbose flag" OFF)
OPTION(TINES_ENABLE_DEBUG "Flag to enable TINES debug flag" OFF)
OPTION(TINES_ENABLE_TRBDF2_WRMS "Flag to enable TINES TrBDF2 to use weighted rms norm for error estimation" ON)
OPTION(TINES_ENABLE_NEWTON_WRMS "Flag to enable TINES TrBDF2 to use weighted rms norm for error estimation" ON)

# use intel compiler and -mkl flag 
OPTION(TINES_ENABLE_MKL "Flag to enable MKL" OFF)

OPTION(KOKKOS_INSTALL_PATH "Path to Kokkos installation")
OPTION(LAPACKE_INSTALL_PATH "Path to LAPACKE installation")
OPTION(OPENBLAS_INSTALL_PATH "Path to OpenBLAS installation")
OPTION(GTEST_INSTALL_PATH "Path to gtest installation")
OPTION(YAML_INSTALL_PATH "Path to YAML installation")
OPTION(SUNDIALS_INSTALL_PATH "Path to SUNDIALS installation")
OPTION(CUBLAS_INSTALL_PATH "Path to CUBLAS installation")

OPTION(TINES_DENSE_LINEAR_ALGEBRA_WARNING "Provides warning if library is specified" ON)
OPTION(TINES_CUDA_WARNING "Provides warning if library is specified" ON)
OPTION(TINES_SUNDIALS_WARNING "Provides warning if library is specified" ON)

INCLUDE_DIRECTORIES(${TINES_BINARY_DIR})

# TINES link libraries for tests
SET(TINES_LINK_LIBRARIES tines)

#
# Kokkos
#
IF(TARGET Kokkos::kokkos)
  # check to see if kokkos was loaded in another project
  message(STATUS "Using available Kokkos::kokkos target")
  SET(TINES_INTERNAL_KOKKOS_TARGET "Kokkos::kokkos")
  SET(TINES_ENABLE_TPL_KOKKOS ON CACHE BOOL "Enable Kokkos")
  LIST(APPEND TINES_LINK_LIBRARIES ${TINES_INTERNAL_KOKKOS_TARGET})
ELSE()
  IF (KOKKOS_INSTALL_PATH)
    MESSAGE(STATUS "TINES uses Kokkos installed at ${KOKKOS_INSTALL_PATH}")
    FIND_PACKAGE(Kokkos REQUIRED)
    SET(TINES_INTERNAL_KOKKOS_TARGET "Kokkos::kokkos")
    SET(TINES_ENABLE_TPL_KOKKOS ON CACHE BOOL "Enable Kokkos")
    LIST(APPEND TINES_LINK_LIBRARIES ${TINES_INTERNAL_KOKKOS_TARGET})
  ELSE()
    MESSAGE(FATAL_ERROR "-- Kokkos is required library but KOKKOS_INSTALL_PATH is not provided")
  ENDIF()
ENDIF()

#
# gtest
#
IF (TINES_ENABLE_TEST)
  IF (GTEST_INSTALL_PATH)
    # Interface installed gtest
    MESSAGE("-- TINES uses gtest installed at ${GTEST_INSTALL_PATH}")
    FIND_PACKAGE(Gtest REQUIRED)
    SET(TINES_INTERNAL_GTEST_TARGET "GTest::gtest_main")
  ELSE()
    MESSAGE(FATAL_ERROR "-- TEST is enabled but GTEST_INSTALL_PATH is not provided")
  ENDIF()
  LIST(APPEND TINES_LINK_LIBRARIES ${TINES_INTERNAL_GTEST_TARGET})
ENDIF()

#
# OpenBLAS
#
IF (OPENBLAS_INSTALL_PATH)
  FIND_PACKAGE (OpenBLAS)
  IF (OPENBLAS_FOUND)
    SET(TINES_ENABLE_TPL_OPENBLAS ON CACHE BOOL "Enable OpenBLAS")
    SET(TINES_INTERNAL_OPENBLAS_TARGET "openblas")
    MESSAGE("-- OpenBLAS is found at ${OPENBLAS_INSTALL_PATH}")
    IF (OPENBLAS_FOUND_CBLAS_HEADER)
      MESSAGE("-- OpenBLAS includes  CBLAS")
      SET(TINES_ENABLE_TPL_OPENBLAS_CBLAS_HEADER ON CACHE BOOL "Enable OpenBLAS from MacPort")
    ENDIF()
    LIST(APPEND TINES_LINK_LIBRARIES openblas)
    IF (LAPACKE_FOUND)
       MESSAGE("-- OpenBLAS includes LAPACKE")
      SET(TINES_ENABLE_TPL_LAPACKE ON CACHE BOOL "Enable LAPACKE from OpenBlas")
    ENDIF()
  ELSE()
    MESSAGE(FATAL_ERROR "-- OpenBLAS is not found at ${OPENBLAS_INSTALL_PATH}")
  ENDIF()
ENDIF()

#
# LAPACKE
#
IF (LAPACKE_INSTALL_PATH)
  IF (NOT TINES_ENABLE_TPL_LAPACKE)
    FIND_PACKAGE (LAPACKE)
    IF (LAPACKE_FOUND)
      MESSAGE("-- LAPACKE is found at ${LAPACKE_INSTALL_PATH}")
      SET(TINES_ENABLE_TPL_LAPACKE ON CACHE BOOL "Enable LAPACKE")
      SET(TINES_INTERNAL_LAPACKE_TARGET "lapacke")
      SET(TINES_INTERNAL_LAPACK_TARGET "lapack")
      LIST(APPEND TINES_LINK_LIBRARIES lapacke)
      LIST(APPEND TINES_LINK_LIBRARIES lapack)
    ELSE()
      MESSAGE(FATAL_ERROR "-- LAPACKE is not found at ${LAPACKE_INSTALL_PATH}")
    ENDIF()
  ELSE()
    MESSAGE("-- LAPACKE is enabled from OpenBlas")
  ENDIF()
ENDIF()

#
# MKL
#
IF (TINES_ENABLE_MKL)
  IF (CMAKE_CXX_COMPILER_ID STREQUAL "Intel")
    SET(TINES_ENABLE_TPL_MKL ON CACHE BOOL "Enable MKL")
    SET(CMAKE_CXX_FLAGS  "${CMAKE_CXX_FLAGS} -mkl")
    SET(CMAKE_EXE_LINKER_FLAGS  "${CMAKE_EXE_LINKER_FLAGS} -mkl")
    MESSAGE("-- MKL is enabled (use intel compiler supporting -mkl)")
  ELSE()
    MESSAGE(FATAL_ERROR "-- MKL is not enabled for ${CMAKE_CXX_COMPILER_ID} (use intel compiler supporting -mkl); MKL is not enabled")
  ENDIF()
ENDIF()

#
# YAML
#

IF(TARGET yaml)
  MESSAGE(STATUS "Using available yaml target")
  SET(TINES_ENABLE_TPL_YAML ON CACHE BOOL "Enable YAML")
  SET(TINES_INTERNAL_YAML_TARGET "yaml")
  LIST(APPEND TINES_LINK_LIBRARIES ${TINES_INTERNAL_YAML_TARGET})
ELSE()
  IF (YAML_INSTALL_PATH)
    # Interface installed YAML
    MESSAGE("-- TINES uses YAML installed at ${YAML_INSTALL_PATH}")
    FIND_PACKAGE(YAML)
    SET(TINES_ENABLE_TPL_YAML ON CACHE BOOL "Enable YAML")
    SET(TINES_INTERNAL_YAML_TARGET "yaml")
    LIST(APPEND TINES_LINK_LIBRARIES ${TINES_INTERNAL_YAML_TARGET})
  ELSE()
    MESSAGE(WARNING "-- YAML_INSTALL_PATH is not provided; YAML TPL is not enabled")
  ENDIF()
ENDIF()
#
# SUNDIALS
#
IF (SUNDIALS_INSTALL_PATH)
  # Interface installed SUNDIALS
  MESSAGE("-- TINES uses SUNDIALS installed at ${SUNDIALS_INSTALL_PATH}")
  FIND_PACKAGE(SUNDIALS)
  SET(TINES_ENABLE_TPL_SUNDIALS ON CACHE BOOL "Enable SUNDIALS")
  SET(TINES_INTERNAL_SUNDIALS_TARGET "sundials")
  LIST(APPEND TINES_LINK_LIBRARIES ${TINES_INTERNAL_SUNDIALS_TARGET})
ELSEIF(TINES_SUNDIALS_WARNING)
  MESSAGE(WARNING "-- SUNDIALS_INSTALL_PATH is not provided; SUNDIALS TPL is not enabled")
ENDIF()

#
# CUDA
#
IF (CUDA_INSTALL_PATH)
  # Interface installed CUDA
  MESSAGE("-- TINES uses CUBLAS installed at ${CUDA_INSTALL_PATH}")
  FIND_PACKAGE(CUDA)
  SET(TINES_ENABLE_TPL_CUBLAS ON CACHE BOOL "Enable CUDA")
  SET(TINES_INTERNAL_CUDA_TARGET "cublas")
  LIST(APPEND TINES_LINK_LIBRARIES ${TINES_INTERNAL_CUDA_TARGET})
ELSEIF(TINES_CUDA_WARNING)
  MESSAGE(WARNING "-- CUDA_INSTALL_PATH is not provided; CUDA TPLs are not enabled")
ENDIF()

#
# Linking libraries used in example and test
#
IF (TINES_DENSE_LINEAR_ALGEBRA_WARNING)
  IF (NOT (TINES_ENABLE_TPL_MKL))
    IF (NOT (TINES_ENABLE_TPL_LAPACKE AND TINES_ENABLE_TPL_OPENBLAS))
      MESSAGE(WARNING "-- There no dense linear algebra packages are detected")
    ENDIF()
  ENDIF()
ENDIF()

MESSAGE("-- TINES_LINK_LIBRARIES = ${TINES_LINK_LIBRARIES}")

#
# Define configure-related preprocessor macros
#
CONFIGURE_FILE (
  ${TINES_SOURCE_DIR}/core/cmake/Tines_Config.hpp.in
  ${TINES_BINARY_DIR}/Tines_Config.hpp
  )
INSTALL(FILES ${CMAKE_CURRENT_BINARY_DIR}/Tines_Config.hpp
  DESTINATION "${CMAKE_INSTALL_PREFIX}/${TINES_INSTALL_INCLUDE_PATH}")


#
# Process subdirectories
#

ADD_SUBDIRECTORY (core)
IF (TINES_ENABLE_TEST AND TINES_ENABLE_EXAMPLE)
  ADD_SUBDIRECTORY (unit-test)
ENDIF()
IF (TINES_ENABLE_EXAMPLE OR TINES_ENABLE_TEST)
  ADD_SUBDIRECTORY (example)
ENDIF()

# add in target include for build to enable finding Tines_Config
TARGET_INCLUDE_DIRECTORIES(tines
        PUBLIC
        $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>)
